// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Matrix.ServiceCommunication.Options;

namespace Matrix.ServiceCommunication.Services;

/// <summary>
/// 客户端凭证认证提供者
/// </summary>
public class ClientCredentialsAuthenticationProvider : IAuthenticationProvider
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IOptionsMonitor<ServiceCommunicationOptions> _options;
    private readonly ILogger<ClientCredentialsAuthenticationProvider> _logger;
    private readonly IMemoryCache _cache;

    public ClientCredentialsAuthenticationProvider(
        IHttpClientFactory httpClientFactory,
        IOptionsMonitor<ServiceCommunicationOptions> options,
        ILogger<ClientCredentialsAuthenticationProvider> _logger,
        IMemoryCache cache)
    {
        _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
        _options = options ?? throw new ArgumentNullException(nameof(options));
        this._logger = _logger ?? throw new ArgumentNullException(nameof(_logger));
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
    }

    public async Task<string> GetAccessTokenAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        var options = _options.CurrentValue;
        var authOptions = options.Authentication;

        if (authOptions.Type == AuthenticationType.None)
        {
            return string.Empty;
        }

        var cacheKey = $"access_token:{serviceName}";
        if (_cache.TryGetValue(cacheKey, out string? cachedToken) && !string.IsNullOrEmpty(cachedToken))
        {
            return cachedToken;
        }

        try
        {
            var token = await RequestAccessTokenAsync(cancellationToken);
            if (!string.IsNullOrEmpty(token))
            {
                // 缓存Token
                var cacheOptions = new MemoryCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(authOptions.TokenCacheSeconds)
                };
                _cache.Set(cacheKey, token, cacheOptions);

                _logger.LogDebug("获取访问Token成功，服务: {ServiceName}", serviceName);
            }

            return token ?? string.Empty;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取访问Token失败，服务: {ServiceName}", serviceName);
            return string.Empty;
        }
    }

    public async Task<string> RefreshTokenAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        // 清除缓存并重新获取
        var cacheKey = $"access_token:{serviceName}";
        _cache.Remove(cacheKey);

        return await GetAccessTokenAsync(serviceName, cancellationToken);
    }

    public Task<bool> ValidateTokenAsync(string token, CancellationToken cancellationToken = default)
    {
        // 简单实现：检查Token是否为空
        // 在实际项目中，这里应该解析JWT或调用验证端点
        return Task.FromResult(!string.IsNullOrEmpty(token));
    }

    private async Task<string> RequestAccessTokenAsync(CancellationToken cancellationToken = default)
    {
        var options = _options.CurrentValue;
        var authOptions = options.Authentication;

        if (string.IsNullOrEmpty(authOptions.TokenEndpoint) ||
            string.IsNullOrEmpty(authOptions.ClientId) ||
            string.IsNullOrEmpty(authOptions.ClientSecret))
        {
            _logger.LogWarning("认证配置不完整，无法获取访问令牌");
            return string.Empty;
        }

        using var client = _httpClientFactory.CreateClient("Authentication");

        var requestData = new Dictionary<string, string>
        {
            { "grant_type", "client_credentials" },
            { "client_id", authOptions.ClientId },
            { "client_secret", authOptions.ClientSecret },
            { "scope", "api.read api.write" }
        };

        var content = new FormUrlEncodedContent(requestData);

        try
        {
            var response = await client.PostAsync(authOptions.TokenEndpoint, content, cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                var errorContent = await response.Content.ReadAsStringAsync(cancellationToken);
                _logger.LogError("获取访问令牌失败: {StatusCode} - {Error}",
                    response.StatusCode, errorContent);
                return string.Empty;
            }

            var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
            var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(responseContent, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            });

            return tokenResponse?.AccessToken ?? string.Empty;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "请求访问令牌时发生异常");
            return string.Empty;
        }
    }

    /// <summary>
    /// Token响应模型
    /// </summary>
    private class TokenResponse
    {
        public string AccessToken { get; set; } = string.Empty;
        public string TokenType { get; set; } = string.Empty;
        public int ExpiresIn { get; set; }
        public string? Scope { get; set; }
    }
}